package com.gz.common.mongodb.dao;

import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.framework.AopProxy;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @ClassName MongoGetTClass
 * @Description // 获取泛型的类型
 * @Author WHX
 * @Date 2021/8/15 17:08
 **/
public interface MongoGetTClass<Model> {
    /**
     * key：spring-data-mongodb对DAO层的代理对象的 hashCode
     * value：该DAO层接口对应的实体类的 Class
     */
    public static final Map<Integer, Class> MODEL_TYPE_MAP = new ConcurrentHashMap<Integer, Class>();

    /**
     * 适用于spring-data-mongodb对DAO层的代理，获取其DAO层接口的第一个父接口的第一个泛型类型
     * @return
     */
    default Class<Model> getTClass(){
        // 从本地缓存获取
        Integer thisHashcode = this.hashCode();
        Class<Model> type = MODEL_TYPE_MAP.get(thisHashcode);
        if (type!=null) return type;

        // 使用反射获取并存入MODEL_TYPE_MAP
        try {
            Field h = this.getClass().getSuperclass().getDeclaredField("h");
            ReflectionUtils.makeAccessible(h);
            AopProxy jdkDynamicAopProxy = (AopProxy)h.get(this);

            Field advised = jdkDynamicAopProxy.getClass().getDeclaredField("advised");
            ReflectionUtils.makeAccessible(advised);
            AdvisedSupport advisedSupport = (AdvisedSupport)advised.get(jdkDynamicAopProxy);

            Field interfacesField = advisedSupport.getClass().getSuperclass().getSuperclass().getDeclaredField("interfaces");
            ReflectionUtils.makeAccessible(interfacesField);
            List<Class<?>> interfaces = (List<Class<?>> )interfacesField.get(advisedSupport);

            Type ModelType = ((ParameterizedType) interfaces.get(0).getGenericInterfaces()[0]).getActualTypeArguments()[0];
            MODEL_TYPE_MAP.put(thisHashcode, (Class<Model>) ModelType);
        } catch (Exception e) {
            throw new RuntimeException("Failed to get the type of the entity class. Please read the annotation for the current method", e);
        }

        return MODEL_TYPE_MAP.get(thisHashcode);
    }
}
